﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Phoenix.Xna.Framework.Collections;

namespace Phoenix.Xna.Framework.Components
{
    public class ComponentCollection : GenericCollection<IComponent>, IDisposableEx
    {
        private List<IDrawableComponent> _drawableComponents;
        private bool _sortOnUpdate;
        private bool _sortOnDraw;

        public ComponentCollection()
            : base()
        {
            _drawableComponents = new List<IDrawableComponent>();
        }

        public void Initialize()
        {
            foreach (IComponent c in InnerList)
            {
                c.Initialize();
            }
        }

        public void Update(GameTime gameTime)
        {
            if (_sortOnUpdate)
            {
                _sortOnUpdate = false;
                InnerList.Sort(new UpdateOrderComparer());
            }

            foreach (IComponent c in InnerList)
            {
                c.Update(gameTime);
            }
        }

        public void LoadContent()
        {
            foreach (IDrawableComponent c in _drawableComponents)
            {
                c.LoadContent();
            }
        }

        public void UnloadContent()
        {
            foreach (IDrawableComponent c in _drawableComponents)
            {
                c.UnloadContent();
            }
        }

        public void Draw(GameTime gameTime)
        {
            if (_sortOnDraw)
            {
                _sortOnDraw = false;
                _drawableComponents.Sort(new DrawOrderComparer());
            }

            foreach (IDrawableComponent c in _drawableComponents)
            {
                c.Draw(gameTime);
            }
        }

        public IComponent FindComponentByID(string id)
        {
            foreach (IComponent c in InnerList)
            {
                if (string.Compare(c.ID, id, false) == 0)
                {
                    return c;
                }
            }

            return null;
        }

        protected virtual void Dispose(bool disposing)
        {
            foreach (IComponent c in InnerList)
            {
                c.Dispose();
            }
        }

        private void OnUpdateOrderChanged(object sender, UpdateOrderChangedEventArgs e)
        {
            _sortOnUpdate = true;
        }

        private void OnDrawOrderChanged(object sender, DrawOrderChangedEventArgs e)
        {
            _sortOnDraw = true;
        }

        #region IList<IComponent> Members

        public override void Insert(int index, IComponent item)
        {
            if (!InnerList.Contains(item))
            {
                item.Initialize();
                base.Insert(index, item);
                item.UpdateOrderChanged += new EventHandler<UpdateOrderChangedEventArgs>(OnUpdateOrderChanged);

                if (item is IDrawableComponent)
                {
                    IDrawableComponent component = item as IDrawableComponent;

                    if (!_drawableComponents.Contains(component))
                    {
                        component.DrawOrderChanged += new EventHandler<DrawOrderChangedEventArgs>(OnDrawOrderChanged);
                        component.LoadContent();
                        _drawableComponents.Add(component);
                    }
                }
            }
        }

        public override void RemoveAt(int index)
        {
            IComponent item = null;

            if (index > 0 && index < InnerList.Count)
            {
                item = InnerList[index];
            }

            item.UpdateOrderChanged -= OnUpdateOrderChanged;
            base.RemoveAt(index);

            if (item != null && item is IDrawableComponent)
            {
                IDrawableComponent component = item as IDrawableComponent;

                if (_drawableComponents.Contains(component))
                {
                    component.DrawOrderChanged -= OnDrawOrderChanged;
                    _drawableComponents.Remove(component);
                }
            }
        }

        public override IComponent this[int index]
        {
            get { return base[index]; }
            set
            {
                IComponent oldValue = InnerList[index];
                value.Initialize();
                base[index] = value;

                if (oldValue is IDrawableComponent)
                {
                    IDrawableComponent component = oldValue as IDrawableComponent;

                    for (int i = 0; i < _drawableComponents.Count; i++)
                    {
                        if (_drawableComponents[i].Equals(component))
                        {
                            _drawableComponents[i] = (IDrawableComponent)value;
                            _drawableComponents[i].LoadContent();
                            break;
                        }
                    }
                }
            }
        }

        #endregion

        #region ICollection<IComponent> Members

        public override void Clear()
        {
            foreach (IComponent c in InnerList)
            {
                c.UpdateOrderChanged -= OnUpdateOrderChanged;
            }

            foreach (IDrawableComponent c in _drawableComponents)
            {
                c.DrawOrderChanged -= OnDrawOrderChanged;
            }

            _drawableComponents.Clear();
            base.Clear();
        }

        public override bool Remove(IComponent item)
        {
            if (InnerList.Contains(item))
            {
                item.UpdateOrderChanged -= OnUpdateOrderChanged;

                if (item is IDrawableComponent)
                {
                    IDrawableComponent component = item as IDrawableComponent;

                    if (_drawableComponents.Contains(component))
                    {
                        component.DrawOrderChanged -= OnDrawOrderChanged;
                        _drawableComponents.Remove(component);
                    }
                }
            }

            return base.Remove(item);
        }

        public override void Add(IComponent item)
        {
            if (item != null && !InnerList.Contains(item))
            {
                item.UpdateOrderChanged += new EventHandler<UpdateOrderChangedEventArgs>(OnUpdateOrderChanged);
                item.Initialize();
                base.Add(item);

                if (item is IDrawableComponent)
                {
                    IDrawableComponent component = item as IDrawableComponent;

                    if (!_drawableComponents.Contains(component))
                    {
                        component.DrawOrderChanged += new EventHandler<DrawOrderChangedEventArgs>(OnDrawOrderChanged);
                        component.LoadContent();
                        _drawableComponents.Add(component);
                    }
                }
            }
        }

        #endregion

        #region IDisposableEx Members

        public bool IsDisposed { get; protected set; }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
